import sqlite3
import inspect
import uuid
import time
import asyncio
from fastapi import FastAPI
from fastapi import Form
from fastapi import Cookie
from fastapi import HTTPException
from typing import Annotated
from fastapi.responses import Response
from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates

from . import config
from . import userdb
from . import session_save
from .userobj import User

SESSION_LIST = {}

LAST_LOGIN_TIME = 0


def check_user(neutron_token: str | None):
    if (neutron_token is None or neutron_token not in SESSION_LIST):
        if (config.read("security")["demo"] == False and config.read("debug")["debug"] == False):
            raise HTTPException(401)
        else:
            user_obj = User("default", "default",
                            config.read("security")["demo"])
    else:
        user_obj = User(SESSION_LIST[neutron_token], neutron_token)
    return user_obj


def check_cookie(func, other_check=None, *other_check_args, **other_check_kwargs):  # 校验身份装饰器
    arg_name = None

    async def warpper(neutron_token: Annotated[str | None, Cookie()], *args, **kwargs):
        nonlocal arg_name
        ret = check_user(neutron_token)
        if (other_check is not None):  # 执行附加检查
            await other_check(ret, *other_check_args, **other_check_kwargs)
        if (arg_name is not None):  # 添加 User 类型参数
            kwargs[arg_name] = ret
        return await func(*args, **kwargs)
    sig = inspect.signature(func)
    finalparams = []
    finalparamslast = []
    for i in list(sig.parameters.values()):  # 处理 User 类型
        if i.annotation is not inspect.Parameter.empty and i.annotation == User:
            arg_name = i.name
        elif (i.kind == i.VAR_POSITIONAL or i.kind == i.VAR_KEYWORD):
            finalparamslast.append(i)
        else:
            finalparams.append(i)
    finalparams.append(inspect.Parameter(
        # 添加 token 参数
        'neutron_token', inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=Annotated[str | None, Cookie()], default=None))
    # 复写函数签名
    warpper.__name__ = func.__name__
    warpper.__doc__ = func.__doc__
    warpper.__module__ = func.__module__
    warpper.__qualname__ = func.__qualname__
    warpper.__signature__ = inspect.Signature(finalparams+finalparamslast)
    return warpper


def conn_users_db():
    return sqlite3.connect("config/users.db")


def main(app: FastAPI, templates: Jinja2Templates):
    LOGIN_TEMPLATE = templates.TemplateResponse(
        "login.html", {"request": {}})  # 预先渲染登录页面

    @app.get("/"+config.read("security")["loginpath"])  # 安全路径
    async def root():
        return LOGIN_TEMPLATE

    @app.post("/api/login")
    async def get_icons(response: Response, username=Form(), password=Form()):  # 登录api
        global LAST_LOGIN_TIME
        if (LAST_LOGIN_TIME + config.read("security")["logintimeout"] > time.time()):
            return {"code": 1, "msg": "登录过快"}
        LAST_LOGIN_TIME = time.time()
        result = await userdb.execute("SELECT password FROM `users` WHERE username = ?", False, (username,))
        if not result:
            return {"code": 1, "msg": "用户不存在"}
        result = result[0]
        if (result[0] == userdb.hash_password(password)):
            session_key = str(uuid.uuid1().hex)
            response.set_cookie("neutron_token", session_key)
            SESSION_LIST[session_key] = username
            return {"code": 0, "msg": ""}
        else:
            return {"code": 1, "msg": "帐号或密码错误"}

    @app.get("/logout")
    @check_cookie
    async def logout(user: User):  # 登出
        if (user.token in SESSION_LIST):
            del SESSION_LIST[user.token]
        return RedirectResponse(url="/"+config.read("security")["loginpath"], status_code=302)

    @app.get("/clean_session")
    @check_cookie
    async def clean_session(user: User):  # 清空会话
        session_save.now_session[user.username] = {
            "windows": []
        }
        asyncio.create_task(userdb.execute(
            "DELETE FROM `session` WHERE username = ?", True, (user.username,)))
        return RedirectResponse(url="/", status_code=302)


if __name__ == "__main__":
    print("This program cannot be run as a script. Please run the main.py file instead.")
